package cn.elwy.rcp.ui.table.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;

import cn.elwy.common.exception.RunException;
import cn.elwy.common.log.LoggerFactory;

/**
 * 反射工具类
 * @author huangsq
 * @version 2.0, 2013-09-09
 * @since 1.0, 2012-02-19
 */
@SuppressWarnings("unchecked")
public final class ReflectUtil {

  private static final Logger logger = LoggerFactory.getLogger(ReflectUtil.class);

  public static final String SPLIT_DOT = "\\.";
  public static final String SPLIT_COLON = ":";
  public static final String SPLIT_VIRGULE = "\\|";
  public static final String DOT = ".";
  public static final String DOT_SPLIT = "\\.";
  public static final String GET = "get";
  public static final String SET = "set";
  public static final String IS = "is";
  public static final String PARENTHESIS = "()";

  private ReflectUtil() {
  }

  /**
   * 关闭资源
   * @param object 要关闭的对象
   */
  public static void close(Object object) {
    try {
      invoke(object, "close");
    } catch (Exception e) {
      logger.warn(e.getMessage(), e);
    }
  }

  /**
   * 释放资源
   * @param object 要释放的对象
   */
  public static void dispose(Object object) {
    try {
      invoke(object, "dispose");
    } catch (Exception e) {
      logger.warn(e.getMessage(), e);
    }
  }

  /**
   * 执行指定对象的方法，方法必需无参数，不抛出错误
   * @param object 执行的对象
   * @param methodName 方法名称
   * @return
   */
  public static <T> T run(Object object, String methodName) {
    T result = null;
    try {
      result = invoke(object, methodName);
    } catch (Exception e) {
      logger.warn(e.getMessage(), e);
    }
    return result;
  }

  /**
   * 执行指定对象的方法，不抛出错误，目标参数类型根据参数决定
   * @param object 被执行对象
   * @param methodName 方法名称
   * @param args 目标方法的参数，参数可以为null
   * @return
   */
  public static <T> T run(Object object, String methodName, Object[] args) {
    T result = null;
    try {
      result = invoke(object, methodName, args);
    } catch (Exception e) {
      logger.warn(e.getMessage(), e);
    }
    return result;
  }

  /**
   * 执行指定对象的方法，不抛出错误
   * @param object 被执行对象
   * @param methodName 方法名称
   * @param args 目标方法的参数
   * @param argsType 参数类型
   * @return
   */
  public static <T> T run(Object object, String methodName, Object[] args, Class<?>[] argsType) {
    T result = null;
    try {
      result = invoke(object, methodName, args, argsType);
    } catch (Exception e) {
      logger.warn(e.getMessage(), e);
    }
    return result;
  }

  /**
   * 获取指定对象的属性值或方法值
   * @param object 被执行对象
   * @param name 属性名称或方法名称(方法名称带括号)
   * @return
   */
  public static <T> T getValue(Object object, String name) {
    if (name.endsWith(PARENTHESIS)) {
      name = name.replace(PARENTHESIS, "");
      return getMethodValue(object, name);
    } else {
      return getFieldValue(object, name);
    }
  }

  /**
   * 根据属性名称获取属性的值，可以是私有属性或父类的属性
   * @param object 被执行对象
   * @param fieldName 属性名称
   * @return
   */
  public static <T> T getFieldValue(Object object, String fieldName) {
    try {
      Field field = getField(object.getClass(), fieldName);
      return (T) field.get(object);
    } catch (Exception e) {
      logger.warn(e.getMessage(), e);
      return null;
    }
  }

  /**
   * 执行指定对象的方法，方法必需无参数，获取返回值
   * @param object 被执行对象
   * @param methodName 方法名称(不带括号)，多个方法用“.”分割
   * @return
   */
  public static <T> T getMethodValue(Object object, String methodName) {
    try {
      return invoke(object, methodName);
    } catch (Exception e) {
      logger.warn(e.getMessage(), e);
      return null;
    }
  }

  /**
   * 执行指定对象的方法，方法必需无参数，获取返回值
   * @param object 被执行对象
   * @param names 属性名称或方法名称(方法名称带括号)，多个名称用“.”分割
   * @return
   */
  public static <T> T getTableColumnValue(Object object, String names, boolean isSupportMap) {
    String[] methods = names.split(SPLIT_DOT);
    for (String method : methods) {
      if (object == null) {
        return null;
      }
      if (isSupportMap && object instanceof Map) {
        object = ((Map<?, ?>) object).get(method);
      } else {
        object = getValue(object, method);
      }
    }
    return (T) object;
  }

  /**
   * 获取对象的值，当isSupportMap=true且对象是一个map时，使用method作为key获取map中的值。
   * 否则利用反射机制执行指定对象的方法，方法必需无参数，获取返回值
   * @param object 被执行对象
   * @param names 属性名称或方法名称(方法名称带括号)，多个对象用“|”分割，格式：UserInfo:getOrg().address.name|Org:name
   * @param isSupportMap 支持方法名作为map的key取值
   * @return
   */
  public static <T> T getTreeColumnValue(Object object, String names, boolean isSupportMap) {
    String[] classNames = names.split(SPLIT_VIRGULE);
    String clazzName = object.getClass().getSimpleName();
    String[] clazzNames = null;
    for (String className : classNames) {
      clazzNames = className.split(SPLIT_COLON);
      if (clazzNames.length == 1) {
        return getTableColumnValue(object, clazzNames[0], isSupportMap);
      } else if (clazzName.equals(clazzNames[0])) {
        return getTableColumnValue(object, clazzNames[1], isSupportMap);
      }
    }
    return getTableColumnValue(object, clazzNames[1], isSupportMap);
  }

  /**
   * 根据属性名称设置属性的值，可以是私有属性或父类的属性
   * @param object 被执行对象
   * @param fieldName 属性名称
   * @param value 设置的值
   */
  public static void setFieldValue(Object object, String fieldName, Object value) {
    try {
      Field field = getField(object.getClass(), fieldName);
      field.set(object, value);
    } catch (Exception e) {
      throw new RunException(e.getMessage(), e);
    }
  }

  /**
   * 根据属性名称设置属性的值，可以是私有属性或父类的属性
   * @param object 被执行对象
   * @param fieldName 属性名称
   * @param value 设置的值
   */
  public static void setProperty(Object object, String fieldName, Object value) {
    try {
      BeanUtils.setProperty(object, fieldName, value);
    } catch (Exception e) {
      throw new RunException(e.getMessage(), e);
    }
  }

  /**
   * 设置对象的值，利用反射机制执行指定对象的方法，将参数值设置给对象
   * @param object 被执行对象
   * @param methodName 方法名称
   * @param value 参数的值，参数类型根据值决定
   */
  public static void setMethodValue(Object object, String methodName, Object value, Class<?> argsType,
      boolean isSupportMap) {
    if (isSupportMap && object instanceof Map) {
      ((Map<String, Object>) object).put(methodName, value);
    } else {
      invoke(object, methodName, new Object[] { value }, new Class<?>[] { argsType });
    }
  }

  /**
   * 根据属性名称设置属性的值，可以是私有属性或父类的属性
   * @param object 被执行对象
   * @param methodNames 属性名称或"方法名.属性名称"
   * @param value 设置的值
   */
  public static void setTableColumnValue(Object object, String methodNames, Object value, boolean isSupportMap) {
    String[] methods = methodNames.split(SPLIT_DOT);
    int length = methods.length - 1;
    for (int i = 0; i <= length; i++) {
      if (object == null) {
        return;
      }
      String method = methods[i];
      if (i == length) {
        if (method.endsWith(PARENTHESIS)) {
          method = method.replace(PARENTHESIS, "");
          if (method.startsWith(GET)) {
            method = method.replaceFirst(GET, "");
          } else if (method.startsWith(IS)) {
            method = method.replaceFirst(IS, "");
          }
        } else if (isSupportMap && object instanceof Map) {
          ((Map<String, Object>) object).put(method, value);
        }
        setProperty(object, method, value);
      } else {
        object = getTableColumnValue(object, method, isSupportMap);
      }
    }
  }

  /**
   * 根据属性名称设置属性的值，可以是私有属性或父类的属性
   * @param object 被执行对象
   * @param methodNames 属性名称或"方法名.属性名称"
   * @param value 设置的值
   */
  public static void setTreeColumnValue(Object object, String methodNames, Object value, boolean isSupportMap) {
    String[] classNames = methodNames.split(SPLIT_VIRGULE);
    String name = object.getClass().getSimpleName();
    String[] propertyName = null;
    for (String className : classNames) {
      propertyName = className.split(SPLIT_COLON);
      if (propertyName.length == 1) {
        setTableColumnValue(object, propertyName[0], value, isSupportMap);
        return;
      } else if (name.equals(propertyName[0])) {
        setTableColumnValue(object, propertyName[1], value, isSupportMap);
        return;
      }
    }
    setTableColumnValue(object, propertyName[1], value, isSupportMap);
  }

  /**
   * 执行指定对象的方法，方法必需无参数
   * @param object 被执行对象
   * @param methodName 方法名称
   * @return
   * @throws Exception
   */
  public static <T> T invoke(Object object, String methodName) {
    if (object == null) {
      return null;
    }
    try {
      Method method = getMethod(object.getClass(), methodName);
      return (T) method.invoke(object);
    } catch (Exception e) {
      throw new RunException(e.getMessage(), e);
    }
  }

  /**
   * 执行指定对象的方法，参数与参数类型不匹配，需要调用：
   * invoke(Object object, String methodName, Object[] args, Class<?>[] argTypes)方法
   * @param object 被执行对象
   * @param methodName 方法名称
   * @param args 目标方法的参数
   * @return
   * @throws Exception
   */
  public static <T> T invoke(Object object, String methodName, Object[] args) {
    if (args == null || args.length == 0) {
      return invoke(object, methodName);
    }
    Class<?>[] argTypes = new Class<?>[args.length];
    for (int i = 0; i < args.length; i++) {
      argTypes[i] = args[i].getClass();
    }
    return invoke(object, methodName, args, argTypes);
  }

  /**
   * 执行指定对象的方法
   * @param object 被执行对象
   * @param methodName 方法名称
   * @param args 目标方法的参数
   * @param argTypes 参数类型
   * @return
   * @throws Exception
   */
  public static <T> T invoke(Object object, String methodName, Object[] args, Class<?>[] argTypes) {
    if (object == null) {
      return null;
    }
    try {
      Method method = getMethod(object.getClass(), methodName, argTypes);
      return (T) method.invoke(object, args);
    } catch (Exception e) {
      throw new RunException(e.getMessage(), e);
    }
  }

  public static Field getField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
    Field field = null;
    try {
      field = clazz.getField(fieldName);
    } catch (NoSuchFieldException e) {
      try {
        field = clazz.getDeclaredField(fieldName);
      } catch (NoSuchFieldException ex) {
        if (clazz.getSuperclass() == null) {
          return field;
        } else {
          return getField(clazz.getSuperclass(), fieldName);
        }
      }
    }
    // 调用private属性的关键一句话
    field.setAccessible(true);
    return field;
  }

  /**
   * 利用递归找一个类的指定方法，如果找不到，去父亲里面找直到最上层Object对象为止。
   * @param clazz 目标类
   * @param methodName 方法名
   * @param argsType 方法参数类型
   * @return 对象的方法
   * @throws NoSuchMethodException
   */
  private static Method getMethod(Class<?> clazz, String methodName, final Class<?>... argsType)
      throws NoSuchMethodException {
    Method method = null;
    try {
      method = clazz.getMethod(methodName, argsType);
    } catch (NoSuchMethodException e) {
      try {
        method = clazz.getDeclaredMethod(methodName, argsType);
      } catch (NoSuchMethodException ex) {
        if (clazz.getSuperclass() == null) {
          return method;
        } else {
          return getMethod(clazz.getSuperclass(), methodName, argsType);
        }
      }
    }
    // 调用private方法的关键一句话
    method.setAccessible(true);
    return method;
  }

}
